package com.taldh.verify.code;

import org.apache.commons.collections.CollectionUtils;

import java.util.ArrayList;
import java.util.List;

public class Redeem {

    static String stringTable = "ABCDEFGHIJKMNPQRSTUVWXYZ23456789";
    final static String password = "dak3le2";

    //从byte转为字符表索引所需要的位数
    final static int convertByteCount = 5;

    public static void main(String[] args) throws Exception {
        long begin = System.currentTimeMillis();
        System.out.println(begin);
        System.out.println("=======================");
        List<String> codes = create((byte) 1, 0,1_0000, 13, password);
        System.out.println(String.format("waste time: %s ms", System.currentTimeMillis() - begin));
//        assert CollectionUtils.isNotEmpty(codes);
//
//        codes.forEach(System.out::println);

        verifyCode("H3TUC2WKHBF4G");
        verifyCode("Y5WSHUIAYDA2I");
        verifyCode("Q2WAAUWSQAACC");
        verifyCode("Q2WAAUWSQAABC");

    }

    /**
     * 生成兑换码
     * 这里每一次生成兑换码的最大数量为int的最大值即2147483647
     */
    public static List<String> create(byte groupId, int maxCodeId, int codeCount, int codeLength, String password) {
        //8位的数据总长度
        int fullCodeLength = codeLength * convertByteCount / 8;
        //随机码对时间和id同时做异或处理
        //类型1，id4，随机码n,校验码1
        //随机码有多少个
        int randCount = fullCodeLength - 6;

        List<String> results = new ArrayList<>(codeCount);

        //如果随机码小于0 不生成
        if (randCount <= 0) {
            return null;
        }

        long limit = maxCodeId + codeCount;
        double byteMax = Math.random() * (Byte.MAX_VALUE * 2 + 1);
        for (int i = maxCodeId; i < limit; i++) {
            //这里使用i作为code的id
            //生成2位随机码 2^16
            byte[] randbytes = new byte[randCount];
            for (int j = 0; j < randCount; j++) {
                randbytes[j] = (byte) byteMax;
            }

            //存储所有数据
            ByteHelper byteHelper = ByteHelper.CreateBytes(fullCodeLength);
            byteHelper.AppendNumber(groupId).AppendNumber(i).AppendBytes(randbytes);

            //计算校验码 这里使用所有数据相加的总和与1000 取余
            byte verify = (byte) (byteHelper.GetSum() % 1000);
            byteHelper.AppendNumber(verify);

            //使用随机码与时间和ID进行异或
            for (int j = 0; j < 5; j++) {
                byteHelper.bytes[j] = (byte) (byteHelper.bytes[j] ^ (byteHelper.bytes[5 + j % randCount]));
            }

            //使用密码与所有数据进行异或来加密数据
            byte[] passwordbytes = password.getBytes();
            for (int j = 0; j < byteHelper.bytes.length; j++) {
                byteHelper.bytes[j] = (byte) (byteHelper.bytes[j] ^ passwordbytes[j % passwordbytes.length]);
            }

            //这里存储最终的数据
            byte[] bytes = new byte[codeLength];

            //按6位一组复制给最终数组
            for (int j = 0; j < byteHelper.bytes.length; j++) {
                for (int k = 0; k < 8; k++) {
                    int sourceIndex = j * 8 + k;
                    int targetIndexX = sourceIndex / convertByteCount;
                    int targetIndexY = sourceIndex % convertByteCount;
                    byte placeval = (byte) Math.pow(2, k);
                    byte val = (byte) ((byteHelper.bytes[j] & placeval) == placeval ? 1 : 0);
                    //复制每一个bit
                    bytes[targetIndexX] = (byte) (bytes[targetIndexX] | (val << targetIndexY));
                }
            }

            StringBuilder result = new StringBuilder();
            //编辑最终数组生成字符串
            for (int j = 0; j < bytes.length; j++) {
                result.append(stringTable.charAt(bytes[j]));
            }
            results.add(result.toString());
        }
        return results;
    }

    /**
     * 验证兑换码
     */
    public static boolean verifyCode(String code) {
        byte[] bytes = new byte[code.length()];

        //首先遍历字符串从字符表中获取相应的二进制数据
        for (int i = 0; i < code.length(); i++) {
            byte index = (byte) stringTable.indexOf(code.charAt(i));
            bytes[i] = index;
        }

        //还原数组
        int fullCodeLength = code.length() * convertByteCount / 8;
        int randCount = fullCodeLength - 6;//随机码有多少个

        byte[] fullBytes = new byte[fullCodeLength];
        for (int j = 0; j < fullBytes.length; j++) {
            for (int k = 0; k < 8; k++) {
                int sourceindex = j * 8 + k;
                int targetIndexX = sourceindex / convertByteCount;
                int targetIndexY = sourceindex % convertByteCount;

                byte placeval = (byte) Math.pow(2, targetIndexY);
                byte val = (byte) ((bytes[targetIndexX] & placeval) == placeval ? 1 : 0);

                fullBytes[j] = (byte) (fullBytes[j] | (val << k));
            }
        }

        //解密，使用密码与所有数据进行异或来加密数据
        byte[] passwordbytes = password.getBytes();
        for (int j = 0; j < fullBytes.length; j++) {
            fullBytes[j] = (byte) (fullBytes[j] ^ passwordbytes[j % passwordbytes.length]);
        }

        //使用随机码与时间和ID进行异或
        for (int j = 0; j < 5; j++) {
            fullBytes[j] = (byte) (fullBytes[j] ^ (fullBytes[5 + j % randCount]));
        }

        //获取校验码 计算除校验码位以外所有位的总和
        int sum = 0;
        for (int i = 0; i < fullBytes.length - 1; i++) {
            sum += fullBytes[i];
        }
        byte verify = (byte) (sum % 1000);

        //校验
        if (verify == fullBytes[fullBytes.length - 1]) {
            System.out.println(code + " : verify success!");
            return true;
        } else {
            System.out.println(code + " : verify failed!");
            return false;
        }

    }

}